from email import header
import json
import os
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

class TO:
    def __init__(self, config:dict={}):
        sns.set_theme(style="whitegrid", font="MiSans,MiSans Normal", font_scale=1.5)

        self.to_result = config.get(
            "to_result",
            "data/output_of_evaluator/TO/TO-1000-20220402212012.json"
        )
        self.nd_result = config.get(
            "nd_result",
            "data/output_of_evaluator/ND/ND-1000-20220402202330.json"
        )
        self.output_dir = config.get("output_dir", "data/fig")

    def cw_result2(self):
        """
        于 2022.05.14 重构
        """
        with open(self.nd_result) as f_nd:
            nd_dict = json.load(f_nd)
        with open(self.to_result) as f_to:
            to_dict = json.load(f_to)

        nd = nd_dict["close_world"]
        to = to_dict["close_world"] 

        nd_report_list, to_report_list = nd["report_list"], to["report_list"]

        model_names = ["NB", "DT", "RF", "KNN"]
        df_list = []

        idx = 0
        for c, report_list in zip(["ND", "TO"], [nd_report_list, to_report_list]):
            for model, report in zip(model_names, report_list):
                df_precision = pd.DataFrame(
                    {
                        "Model": model,
                        "Class": c,
                        "Metric": "Precision",
                        "Score": report["macro_avg"]["precision"],
                    }, index=[idx]
                )
                df_list.append(df_precision)
                idx += 1
                df_recall = pd.DataFrame(
                    {
                        "Model": model,
                        "Class": c,
                        "Metric": "Recall",
                        "Score": report["macro_avg"]["recall"],
                    }, index=[idx]
                )
                df_list.append(df_recall)
                idx += 1

        df = pd.concat(df_list)
        df_precision = df[df["Metric"]=="Precision"]
        df_recall = df[df["Metric"]=="Recall"]
        fig1, ax1 = plt.subplots(figsize=(8,6))
        fig2, ax2 = plt.subplots(figsize=(8,6))

        sns.barplot(
            x="Model",
            y="Score",
            hue="Class",
            data=df_precision,
            ax=ax1
        )
        ax1.set(
            ylim=(0,1),
            xlabel="模型",
            ylabel="宏观精准率",
        )
        
        sns.barplot(
            x="Model",
            y="Score",
            hue="Class",
            data=df_recall,
            ax=ax2
        )
        ax2.set(
            ylim=(0,1),
            xlabel="模型",
            ylabel="宏观召回率",
        )

        for ax in [ax1, ax2]:
            for c in ax.containers:
                labels = [f'{round(v.get_height(), 3):.3f}' for v in c]
                ax.bar_label(c, labels=labels, label_type='edge', fontsize=16)
            ax.legend().set_title("")
            sns.move_legend(ax, "upper right")

        fig1.savefig(os.path.join(self.output_dir, "TO_cw_precision.svg"), format="svg", bbox_inches="tight")
        fig2.savefig(os.path.join(self.output_dir, "TO_cw_recall.svg"), format="svg", bbox_inches="tight")

    def cw_result(self):
        with open(self.nd_result) as f_nd:
            nd_dict = json.load(f_nd)
        with open(self.to_result) as f_to:
            to_dict = json.load(f_to)

        nd = nd_dict["close_world"]
        to = to_dict["close_world"] 

        nd_report_list, to_report_list = nd["report_list"], to["report_list"]

        model_names = ["NB", "DT", "RF", "KNN"]
        df_list = []

        idx = 0
        for c, report_list in zip(["ND", "TO"], [nd_report_list, to_report_list]):
            for model, report in zip(model_names, report_list):
                df_precision = pd.DataFrame(
                    {
                        "Model": model,
                        "Class": c,
                        "Metric": "Precision",
                        "Score": report["macro_avg"]["precision"],
                    }, index=[idx]
                )
                df_list.append(df_precision)
                idx += 1
                df_recall = pd.DataFrame(
                    {
                        "Model": model,
                        "Class": c,
                        "Metric": "Recall",
                        "Score": report["macro_avg"]["recall"],
                    }, index=[idx]
                )
                df_list.append(df_recall)
                idx += 1

        df = pd.concat(df_list)
        g = sns.catplot(
            kind="bar",
            x="Model",
            y="Score",
            hue="Class",
            col="Metric",
            saturation=.5,
            data=df,
            height=6,
            legend_out=False,
        )
        g.set(
            ylim=(0,1),
            xlabel="模型",
            ylabel="指标值",
        ).despine(left=True)
        # g.set_titles("{col_name}").set(ylim=(0,1)).despine(left=True)
        titles = ["", ""]
        for ax, title in zip(g.axes.flatten(), titles):
            ax.set_title(title, x=0.5,y=-0.25)

        g._legend.set_title(None)

        # plt.legend(loc="upper right")
        
        ax = g.facet_axis(0,0)
        
        for ax in [g.facet_axis(0,0), g.facet_axis(0,1)]:
            for c in ax.containers:
                labels = [f'{round(v.get_height(), 4):.4f}' for v in c]
                ax.bar_label(c, labels=labels, label_type='edge', fontsize=12)

        img_path = os.path.join(self.output_dir, "TO_cw.svg")
        plt.savefig(img_path, format="svg", bbox_inches="tight")
        return img_path

    def ow_result(self):
        with open(self.nd_result) as f_nd:
            nd_dict = json.load(f_nd)
        with open(self.to_result) as f_to:
            to_dict = json.load(f_to)
        
        nd = nd_dict["open_world"]
        to = to_dict["open_world"]

        avg_precision = {
            "ND":nd["avg_precision"],
            "TO":to["avg_precision"],
        }

        avg_recall = {
            "ND":nd["avg_recall"],
            "TO":to["avg_recall"],
        }

        df_precision = pd.DataFrame(avg_precision, index=["Score"]).T
        df_precision.loc[:,"Metric"] = "Precision"
        df_recall = pd.DataFrame(avg_recall, index=["Score"]).T
        df_recall.loc[:,"Metric"] = "Recall"

        df = pd.concat((df_precision, df_recall), axis=0)
        df.loc["ND","Class"] = "ND"
        df.loc["TO","Class"] = "TO"

        fig, ax = plt.subplots(figsize=(8,6))
        sns.barplot(
            x="Metric",
            y="Score",
            hue="Class",
            data=df,
            ax=ax
        )

        ax.set(
            ylim=(0,1),
            xlabel="指标",
            ylabel="指标值",
            xticklabels=["宏观精准率", "宏观召回率"],
        )
        ax.legend().set_title("")
        sns.move_legend(ax, "upper right")
        
        for c in ax.containers:
            labels = [f'{round(v.get_height(), 3):.3f}' for v in c]
            ax.bar_label(c, labels=labels, label_type='edge')
        
        img_path = os.path.join(self.output_dir, "TO_ow.svg")
        plt.savefig(img_path, format="svg", bbox_inches="tight")
        return img_path

class API:
    def __init__(self, config:dict={}):
        sns.set_theme(style="whitegrid", font="MiSans,MiSans Normal", font_scale=1.5)
        # sns.set_theme(style="ticks", font="MiSans,MiSans Normal", font_scale=1.5)

        self.output_dir = config.get("output_dir", "data/fig")
        self.nd_result = config.get(
            "nd_result",
            "data/output_of_evaluator/ND/ND-1000-20220402202330.json"
        )

        self.window_result_dict = config.get(
            "window_result_dict", {
                50: "data/output_of_evaluator/API/window/API-1000-20220427211956.json", # 50
                100: "data/output_of_evaluator/API/window/API-1000-20220427212420.json", # 100
                150: "data/output_of_evaluator/API/window/API-1000-20220427212817.json", # 150
                200: "data/output_of_evaluator/API/window/API-1000-20220427213223.json", # 200
            }
        )
        self.k_result_dict = config.get(
            "k_result_dict", {
                0.001: "data/output_of_evaluator/API/k/API-1000-20220427221955.json",
                0.002: "data/output_of_evaluator/API/k/API-1000-20220427222355.json",
                0.003: "data/output_of_evaluator/API/k/API-1000-20220427222806.json",
                0.004: "data/output_of_evaluator/API/k/API-1000-20220427223215.json",
                0.005: "data/output_of_evaluator/API/k/API-1000-20220427223624.json",
            }
        )
        self.psub_result_dict = config.get(
            "psub_result_dict", {
                114: "data/output_of_evaluator/API/psub/API-1000-20220428154342.json",                
                314: "data/output_of_evaluator/API/psub/API-1000-20220428154718.json",                
                514: "data/output_of_evaluator/API/psub/API-1000-20220428155059.json",                
                714: "data/output_of_evaluator/API/psub/API-1000-20220428155504.json",                
                914: "data/output_of_evaluator/API/psub/API-1000-20220428155855.json",                
                1114: "data/output_of_evaluator/API/psub/API-1000-20220428160254.json",                
                1314: "data/output_of_evaluator/API/psub/API-1000-20220428160647.json",                
                1514: "data/output_of_evaluator/API/psub/API-1000-20220428161042.json",                                 
            }
        )
        self.mode_result_dict = config.get(
            "mode_result_dict", {
                "all": "data/output_of_evaluator/API/mode/API-1000-20220428164711.json",
                "client2server": "data/output_of_evaluator/API/mode/API-1000-20220428163938.json",
                "server2client": "data/output_of_evaluator/API/mode/API-1000-20220428164310.json",
            }
        )
    
        self.abc_result_dict = config.get(
            "abc_result_dict", {
                "API-A": "data/output_of_evaluator/API/ABC/API-1000-20220402220021.json",
                "API-B": "data/output_of_evaluator/API/ABC/API-1000-20220402220501.json",
                "API-C": "data/output_of_evaluator/API/ABC/API-1000-20220402220845.json",
                "TO": "data/output_of_evaluator/TO/TO-1000-20220402212012.json",
                "ND": "data/output_of_evaluator/ND/ND-1000-20220402202330.json",
            }
        )
    
    def cw_influence(self, var_type:str):
        model_names = ["NB", "DT", "RF", "KNN"]

        if var_type == "window":
            result_dict = self.window_result_dict
            x1, x2 = 50, 200
            y1, y2 = 0, 0.6
            xticks = [50, 100, 150, 200]
        elif var_type == "k":
            result_dict = self.k_result_dict
            x1, x2 = 0.001, 0.005
            y1, y2 = 0, 0.5
            xticks = [0.001, 0.002, 0.003, 0.004, 0.005]
        elif var_type =="psub":
            result_dict = self.psub_result_dict
            x1, x2 = 114, 1514
            y1, y2 = 0, 1
            xticks = [114, 314, 514, 714, 914, 1114, 1314, 1514]
        elif var_type == "mode":
            result_dict = self.mode_result_dict
            y1, y2 = 0, 1

        api_dict_list = []
        for var, filepath in result_dict.items():
            with open(filepath) as f:
                api_dict = json.load(f)
                api_dict_list.append({
                    var: api_dict["close_world"]["report_list"]
                })

        df_list = []
        idx = 0

        for d in api_dict_list:
            for var, report_list in d.items():
                for model, report in zip(model_names, report_list):
                    df_precision = pd.DataFrame({
                        "Model": model,
                        "Class": "API",
                        "Metric": "Precision",
                        "Score": float(report["macro_avg"]["precision"]),
                        "{}".format(var_type): float(var) if var_type != 'mode' else str(var),
                    }, index=[idx])
                    df_list.append(df_precision)
                    idx += 1
                    df_recall = pd.DataFrame({
                        "Model": model,
                        "Class": "API",
                        "Metric": "Recall",
                        "Score": float(report["macro_avg"]["recall"]),
                        "{}".format(var_type): float(var) if var_type != 'mode' else str(var),
                    }, index=[idx])
                    df_list.append(df_recall)
                    idx += 1
        
        df = pd.concat(df_list)
        # print(df)

        fig1, ax1 = plt.subplots(figsize=(8,6))
        fig2, ax2 = plt.subplots(figsize=(8,6))

        if var_type != "mode":
            sns.lineplot(
                data=df[df["Metric"]=="Precision"],
                x=var_type,
                y="Score",
                hue="Model",
                style="Model",
                markers=True,
                linewidth=3,
                markersize=15,
                ax=ax1,
                clip_on=False,
            )
            ax1.set(
                xlim=(x1, x2),
                ylim=(y1, y2),
                xlabel=var_type,
                ylabel="宏观精准率",
                xticks=xticks,
            )
            
            sns.lineplot(
                data=df[df["Metric"]=="Recall"],
                x=var_type,
                y="Score",
                hue="Model",
                style="Model",
                markers=True,
                linewidth=3,
                markersize=15,
                ax=ax2,
                clip_on=False,
            )
            ax2.set(
                xlim=(x1, x2),
                ylim=(y1, y2),
                xlabel=var_type,
                ylabel="宏观召回率",
                xticks=xticks,
            )
        else:
            sns.barplot(
                x="{}".format(var_type),
                y="Score",
                hue="Model",
                data=df[df["Metric"]=="Precision"],
                ax=ax1
            )
            ax1.set(
                ylim=(y1, y2),
                xlabel="插入模式",
                ylabel="宏观精准率",
            )
            
            sns.barplot(
                x="{}".format(var_type),
                y="Score",
                hue="Model",
                data=df[df["Metric"]=="Recall"],
                ax=ax2
            )
            ax2.set(
                ylim=(y1, y2),
                xlabel="插入模式",
                ylabel="宏观召回率",
            )
            for ax in [ax1, ax2]:
                for c in ax.containers:
                    labels = [f'{round(v.get_height(), 3):.3f}' for v in c]
                    ax.bar_label(c, labels=labels, label_type='edge', fontsize=12)


        for ax in [ax1, ax2]:
            ax.legend().set_title("")
            sns.move_legend(ax, "upper right")

        fig1.savefig(os.path.join(self.output_dir, "API_{}_precision.svg".format(var_type)), format="svg", bbox_inches="tight")
        fig2.savefig(os.path.join(self.output_dir, "API_{}_recall.svg".format(var_type)), format="svg", bbox_inches="tight")

    def cw_cmp(self):
        model_names = ["NB", "DT", "RF", "KNN"]
        api_dict_list = []
        for name, filepath in self.abc_result_dict.items():
            with open(filepath) as f:
                api_dict = json.load(f)
                api_dict_list.append({
                    name: api_dict["close_world"]["report_list"]
                })

        df_list = []
        idx = 0

        for d in api_dict_list:
            for name, report_list in d.items():
                for model, report in zip(model_names, report_list):
                    df_precision = pd.DataFrame({
                        "Model": model,
                        "Class": "API",
                        "Metric": "Precision",
                        "Score": report["macro_avg"]["precision"],
                        "Name": str(name),
                    }, index=[idx])
                    df_list.append(df_precision)
                    idx += 1
                    df_recall = pd.DataFrame({
                        "Model": model,
                        "Class": "API",
                        "Metric": "Recall",
                        "Score": report["macro_avg"]["recall"],
                        "Name": str(name),
                    }, index=[idx])
                    df_list.append(df_recall)
                    idx += 1

        df = pd.concat(df_list)
        # g = sns.catplot(
        #     kind="bar",
        #     x="Score",
        #     y="Name",
        #     hue="Model",
        #     row="Metric",
        #     # saturation=.5,
        #     data=df,
        #     height=6,
        #     aspect=1.5,
        #     orient="h",
        #     legend_out=False,
        # )

        # g.set_titles("{row_name}").set(xlim=(0,1))
        # ax_ = g.facet_axis(0,0), g.facet_axis(1,0)
        # for ax in ax_:
        #     for c in ax.containers:
        #         labels = [f'{(round(v.get_width(), 3)):.3f}' for v in c]
        #         ax.bar_label(c, labels=labels, label_type='edge')

        fig1, ax1 = plt.subplots(figsize=(8,6))
        fig2, ax2 = plt.subplots(figsize=(8,6))
        sns.barplot(
            x="Score",
            y="Name",
            hue="Model",
            data=df[df["Metric"]=="Precision"],
            ax=ax1
        )
        ax1.set(
            xlim=(0, 1),
            xlabel="宏观精准率",
            ylabel="抵御分析策略",
        )
        
        sns.barplot(
            x="Score",
            y="Name",
            hue="Model",
            data=df[df["Metric"]=="Recall"],
            ax=ax2
        )
        ax2.set(
            xlim=(0, 1),
            xlabel="宏观召回率",
            ylabel="抵御分析策略",
        )
        for ax in [ax1, ax2]:
            ax.legend().set_title("")
            for c in ax.containers:
                labels = [f'{(round(v.get_width(), 3)):.3f}' for v in c]
                ax.bar_label(c, labels=labels, label_type='edge', fontsize=15)

        fig1.savefig(os.path.join(self.output_dir, "API_TO_ND_cw_precision.svg"), format="svg", bbox_inches="tight")
        fig2.savefig(os.path.join(self.output_dir, "API_TO_ND_cw_recall.svg"), format="svg", bbox_inches="tight")

    def ow_cmp(self):
        model_names = ["RF"]
        api_dict_list = []
        for name, filepath in self.abc_result_dict.items():
            with open(filepath) as f:
                api_dict = json.load(f)
                api_dict_list.append({
                    name: api_dict["open_world"]
                })


        df_list = []
        idx = 0
        model = "RF"
        for d in api_dict_list:
            for name, report in d.items():
                df_precision = pd.DataFrame({
                    "Model": model,
                    "Class": "API",
                    "Metric": "Precision",
                    "Score": report["avg_precision"],
                    "Name": str(name),
                }, index=[idx])
                df_list.append(df_precision)
                idx += 1
                df_recall = pd.DataFrame({
                    "Model": model,
                    "Class": "API",
                    "Metric": "Recall",
                    "Score": report["avg_recall"],
                    "Name": str(name),
                }, index=[idx])
                df_list.append(df_recall)
                idx += 1

        df = pd.concat(df_list)

        # g = sns.catplot(
        #     kind="bar",
        #     x="Name",
        #     y="Score",
        #     # hue="Class",
        #     col="Metric",
        #     saturation=.5,
        #     data=df,
        #     height=6,
        #     # aspect=1.5,
        # )
        # g.set_titles("{col_name}").set(ylim=(0,1)).despine(left=True)
        # ax_ = g.facet_axis(0,0), g.facet_axis(0,1)
        # for ax in ax_:
        #     for c in ax.containers:
        #         labels = [f'{(round(v.get_height(), 4)):.4f}' for v in c]
        #         ax.bar_label(c, labels=labels, label_type='edge')

        fig1, ax1 = plt.subplots(figsize=(8,6))
        fig2, ax2 = plt.subplots(figsize=(8,6))
        sns.barplot(
            x="Name",
            y="Score",
            # hue="Class",
            data=df[df["Metric"]=="Precision"],
            ax=ax1
        )
        ax1.set(
            ylim=(0, 1),
            xlabel="抵御分析策略",
            ylabel="宏观精准率",
        )
        
        sns.barplot(
            x="Name",
            y="Score",
            # hue="Class",
            data=df[df["Metric"]=="Recall"],
            ax=ax2
        )
        ax2.set(
            ylim=(0, 1),
            xlabel="抵御分析策略",
            ylabel="宏观召回率",
        )
        for ax in [ax1, ax2]:
            for c in ax.containers:
                labels = [f'{(round(v.get_height(), 3)):.3f}' for v in c]
                ax.bar_label(c, labels=labels, label_type='edge')

        fig1.savefig(os.path.join(self.output_dir, "API_TO_ND_ow_precision.svg"), format="svg", bbox_inches="tight")
        fig2.savefig(os.path.join(self.output_dir, "API_TO_ND_ow_recall.svg"), format="svg", bbox_inches="tight")

def main():
    # t = TO()
    # t.cw_result()
    a = API()
    a.cw_window_result()

if __name__ == "__main__":
    main()